<?php
// +----------------------------------------------------------------------
// | Module 模块服务
// +----------------------------------------------------------------------
// | 主要服务于Module数据处理
// +----------------------------------------------------------------------
// | 错误编码头 238xxx
// +----------------------------------------------------------------------

namespace common\service;


use common\model\EntityModel;
use common\model\FieldModel;
use common\model\ModuleModel;
use common\model\ModuleRelationModel;
use common\model\SchemaModel;
use common\model\TaskModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\module\Module;

class ModuleService
{
    use SingletonTrait;

    /**
     * _ 名称转驼峰
     * @param $unCamelizeWords
     * @param string $separator
     * @return mixed
     */
    public function camelize($unCamelizeWords, $separator = '_')
    {
        $unCamelizeWords = $separator . str_replace($separator, " ", strtolower($unCamelizeWords));
        return str_replace(" ", "", ucwords(ltrim($unCamelizeWords, $separator)));
    }


    /**
     * 判断当前字段是否为必须
     * @param $field
     * @return string
     */
    public function checkFieldRequire($field)
    {
        if (in_array($field, ['name', 'phone', 'password', 'value', 'ptype', 'status', 'man_hour', 'type', 'attribute_value'])) {
            return 'yes';
        }

        if (strpos($field, '_id')) {
            return 'yes';
        }

        return 'no';
    }

    /**
     * 判断当前字段是否能编辑
     * @param $field
     * @return string
     */
    public function checkFieldEdit($field)
    {
        if (in_array($field, ['id', 'uuid', 'created_by', 'created', 'json', 'is_horizontal'])) {
            return 'deny';
        }

        if (strpos($field, '_id')) {
            return 'deny';
        }

        return 'allow';
    }

    /**
     * 判断当前字段是否能显示
     * @param $field
     * @return string
     */
    public function checkFieldShow($field)
    {
        if (in_array($field, ['json', 'password'])) {
            return 'no';
        }

        return 'yes';
    }


    /**
     * 判断当前字段是否能排序
     * @param $field
     * @return string
     */
    public function checkFieldSort($field)
    {
        if (in_array($field, ['name', 'code', 'attribute_id', 'start_time', 'end_time', 'type', 'created_by', 'created', 'project_id', 'category_id', 'step_category_id'])) {
            return 'allow';
        }


        return 'deny';
    }


    /**
     * 判断当前字段是否能分组
     * @param $field
     * @return string
     */
    public function checkFieldGroup($field)
    {
        if (strpos($field, '_id')) {
            return 'allow';
        }

        return 'deny';
    }

    /**
     * 判断当前字段是否能过滤
     * @param $field
     * @return string
     */
    public function checkFieldFilter($field)
    {
        if (in_array($field, ['id', 'uuid', 'json', 'config', 'param', 'admin_password', 'node_config'])) {
            return 'deny';
        }

        return 'allow';
    }

    /**
     * 判断当前字段是否能过滤
     * @param $field
     * @return string
     */
    public function checkFieldPrimaryKey($field)
    {
        if ($field === 'id') {
            return 'yes';
        }

        return 'no';
    }

    /**
     * 判断当前字段是否能过滤
     * @param $field
     * @return string
     */
    public function checkFieldForeignKey($field)
    {
        if (strpos($field, '_id')) {
            return 'yes';
        }

        return 'no';
    }

    /**
     * 获取固定字段的编辑器类型
     * @param $field
     * @param $type
     * @return string
     */
    public function getFixedFieldEditor($field, $type)
    {
        if (
            strpos($field, '_id')
            || strpos($field, 'enum')
            || in_array($field, ['created_by', 'is_horizontal', 'resolution', 'delivery_platform', 'ptype', 'assignee', 'executor'])
        ) {
            return 'select';
        }

        if (
            strpos($field, '_time')
            || in_array($field, ['created'])
        ) {
            return 'date';
        }

        if (in_array($field, ['ssl', 'tls'])) {
            return 'switch';
        }

        if (
            strpos($type, 'varchar')
            || strpos($type, 'char')
            || strpos($type, 'int')
        ) {
            return 'input';
        }

        if (in_array($type, ['text', 'longtext'])) {
            return 'text_area';
        }


        return 'none';
    }

    /**
     * 生成字段配置
     * @param $modelName
     * @param $realName
     * @param $moduleID
     * @param $field
     * @param $param
     * @return array
     */
    public function generateFieldConfig($modelName, $realName, $moduleID, $field, $param)
    {
        // 默认 id 字段就是主键
        $isPrimary = $field === 'id' ? "yes" : "no";

        // 默认 带_id的参数都属于外键
        $isForeign = strpos($field, '_id') === false ? "no" : "yes";

        if (array_key_exists('data_source', $param)) {
            $dataSource = $param['data_source'];
        } else {
            $dataSource = [ // 数据源
                "type" => "fixed", // 数据源类型，fixed 固定 , dynamic 动态
                "data" => [] // 数据源，静态直接配置，动态是一个字符串标识
            ];
        }


        $fieldConfig = [
            "id" => 0, // 字段id, 固定字段是0，自定义字段是注册的id值
            "field" => $field, // 字段名
            "type" => $param['type'], //字段类型
            "field_type" => "built_in", //字段类型 built_in：固定字段，custom：自定义字段
            "disabled" => "no", // 是否禁用（yes, no）
            "require" => $this->checkFieldRequire($field), // 是否必须（yes, no）
            "table" => $modelName, // 所属表名
            "module_code" => $realName, // 所属模块名
            "module_id" => $moduleID, // 模块id
            "lang" => strtoupper($field), // 语言包KEY
            "editor" => $this->getFixedFieldEditor($field, $param['type']), // 编辑器类型
            "edit" => $this->checkFieldEdit($field), // 是否可以编辑（allow, deny）
            "show" => $this->checkFieldShow($field), // 是否在前台显示 （yes, no）
            "sort" => $this->checkFieldSort($field), // 是否可以排序（allow, deny）
            "group" => $this->checkFieldGroup($field), // 是否可以分组
            "group_name" => "", // 分组显示名称
            "filter" => $this->checkFieldFilter($field), // 是否可以过滤（allow, deny）
            "multiple" => "no", // 是否可以多选（yes, no）
            "format" => [], // 格式化配置
            "validate" => "", // 验证方法
            "mask" => "", // 掩码配置
            "is_primary_key" => $isPrimary, // 是否是主键（yes, no）
            "is_foreign_key" => $isForeign, // 是否是外键（yes, no）
            "placeholder" => "no", // 输入框占位文本 （yes, no）
            "show_word_limit" => "no", // 是否显示输入字数统计 （yes, no）
            "autocomplete" => "no", // 是否自动补全 （yes, no）
            "value_icon" => "", // 值图标
            "label_icon" => "", // 文本图标
            "label_width" => 0, // 文本宽度
            "value_width" => 0, //  值宽度
            "is_label" => "no",  //  是否显示文本 （yes, no）
            "default_value" => "", // 默认值
            "data_source" => $dataSource
        ];

        return $fieldConfig;
    }

    /**
     * 生成关联节点配置
     * @param $schemaId
     * @param $moduleRelationConfig
     * @param $moduleMap
     * @return array
     * @throws \Exception
     */
    public function generateNodeConfig($schemaId, $moduleRelationConfig, $moduleMap)
    {
        $config = [
            'type' => $moduleRelationConfig['type'],
            'src_module_id' => $moduleMap[$moduleRelationConfig['src_module_code']]['id'],
            'dst_module_id' => $moduleMap[$moduleRelationConfig['dst_module_code']]['id'],
            'link_id' => join(',', $moduleRelationConfig['link_id']),
            'schema_id' => $schemaId,
            'uuid' => create_uuid(),
            'node_config' => json_encode([
                "edges" => [
                    "data" => [
                        "type" => "connection",
                        "label" => $moduleRelationConfig['type']
                    ],
                    "source" => $moduleMap[$moduleRelationConfig['src_module_code']]['uuid'],
                    "target" => $moduleMap[$moduleRelationConfig['dst_module_code']]['uuid']
                ],
                "node_data" => [
                    "source" => [
                        "h" => "80px",
                        "w" => "120px",
                        "id" => $moduleMap[$moduleRelationConfig['src_module_code']]['uuid'],
                        "top" => "147px",
                        "left" => "405px",
                        "name" => ucfirst($moduleRelationConfig['src_module_code']),
                        "type" => "module",
                        "icon" => "",
                        "module_id" => $moduleMap[$moduleRelationConfig['src_module_code']]['id'],
                        "module_code" => $moduleRelationConfig['src_module_code'],
                        "module_type" => $moduleMap[$moduleRelationConfig['src_module_code']]['type']
                    ],
                    "target" => [
                        "h" => "80px",
                        "w" => "120px",
                        "id" => $moduleMap[$moduleRelationConfig['dst_module_code']]['uuid'],
                        "top" => "387px",
                        "left" => "229px",
                        "name" => ucfirst($moduleRelationConfig['dst_module_code']),
                        "type" => "module",
                        "icon" => "",
                        "module_id" => $moduleMap[$moduleRelationConfig['dst_module_code']]['id'],
                        "module_code" => $moduleRelationConfig['dst_module_code'],
                        "module_type" => $moduleMap[$moduleRelationConfig['dst_module_code']]['type']
                    ]
                ]
            ])
        ];

        return $config;
    }

    /**
     * 生成实体数据结构模板
     * @param $moduleCode
     * @param array $parentModuleConfig
     * @return array
     * @throws \Exception
     */
    protected function generateEntitySchemaTemplate($moduleCode, $parentModuleConfig = ['child' => '', 'parent' => ''])
    {
        $schemaData = [
            'schema' => [
                'name' => ucfirst($moduleCode),
                'code' => $moduleCode,
                'type' => 'system',
                'uuid' => create_uuid()
            ],
            'module_relation' => [
                [ // 关联项目
                    'src_module_code' => $moduleCode,
                    'dst_module_code' => 'project',
                    'type' => 'belong_to',
                    'link_id' => ['project_id']
                ],
                [ // 关联模块
                    'src_module_code' => $moduleCode,
                    'dst_module_code' => 'module',
                    'type' => 'belong_to',
                    'link_id' => ['module_id']
                ],
                [ // 关联用户
                    'src_module_code' => $moduleCode,
                    'dst_module_code' => 'user',
                    'type' => 'belong_to',
                    'link_id' => ['created_by']
                ],
                [ // 关联反馈
                    'src_module_code' => $moduleCode,
                    'dst_module_code' => 'note',
                    'type' => 'has_many',
                    'link_id' => ['link_id', 'module_id']
                ],
                [ // 关联任务
                    'src_module_code' => $moduleCode,
                    'dst_module_code' => 'task',
                    'type' => 'has_many',
                    'link_id' => ['entity_id', 'entity_module_id']
                ],
                [ // 关联工序流
                    'src_module_code' => $moduleCode,
                    'dst_module_code' => 'step_workflow',
                    'type' => 'belong_to',
                    'link_id' => ['step_workflow_id']
                ]
            ]
        ];


        if (!empty($parentModuleConfig['child'])) {
            // 所属 entity 子集
            array_unshift($schemaData['module_relation'], [
                'src_module_code' => $moduleCode,
                'dst_module_code' => $parentModuleConfig['child'],
                'type' => 'has_many',
                'link_id' => ['entity_id,entity_module_id']
            ]);
        }

        if (!empty($parentModuleConfig['other'])) {
            foreach ($parentModuleConfig['other'] as $otherItem) {
                $schemaData['module_relation'][] = $otherItem;
            }
        }

        return $schemaData;
    }


    /**
     * 组装要新增的自定义字段 media、status、tag
     * @param $moduleId
     * @param $moduleCode
     * @param $moduleMap
     * @return array
     * @throws \Exception
     */
    protected function generateDefaultCustomFields($moduleId, $moduleCode, $moduleMap)
    {
        $moduleParam = [
            'table' => 'entity',
            'relation' => [
                'media' => 'has_one',
                'status' => 'has_one',
                'tag' => 'has_many'
            ]
        ];

        $table = $moduleParam['table'];
        $fieldData = [
            'is_horizontal' => 1,
            'module_id' => $moduleId,
            'type' => 'custom',
            'table' => $table,
            'uuid' => create_uuid()
        ];
        $modelName = $this->camelize($table);

        $rows = [];
        foreach ($moduleParam['relation'] as $relationModule => $relationType) {
            $field = "{$moduleCode}_{$relationModule}";

            $dataSource = [ // 数据源
                "type" => "api", // 数据源类型，fixed 固定 , api 动态
                "data" => [$relationModule], // 数据源，静态直接配置，动态是一个字符串标识
                "src_module_id" => $moduleId,
                "dst_module_id" => $moduleMap[$relationModule]['id'],
                "relation_type" => $relationType
            ];

            $fieldConfig = $this->generateFieldConfig($modelName, $moduleCode, $moduleId, $field, [
                'type' => 'horizontal_relation',
                'data_source' => $dataSource
            ]);
            $fieldData['config'] = json_encode($fieldConfig);
            $rows[] = $fieldData;
        }

        return $rows;
    }

    /**
     * 创建当前模块数据结构
     * @param $moduleCode
     * @param $param
     * @throws \Exception
     */
    protected function createSchema($moduleCode, $param, $moduleMap)
    {
        $parent = '';
        if (array_key_exists('parent', $param)) {
            if (array_key_exists($param['parent'], $moduleMap)) {
                $parent = $param['parent'];
            } else {
                throw_strack_exception('The parent module does not exist.', ErrorCode::PARENT_MODULE_NOT_EXIST);
            }
        }

        $child = '';
        if (array_key_exists('child', $param)) {
            if (array_key_exists($param['child'], $moduleMap)) {
                $child = $param['child'];
            } else {
                throw_strack_exception('The child module does not exist.', ErrorCode::CHILD_MODULE_NOT_EXIST);
            }
        }

        $parentModuleConfig = [
            'child' => $child,
            'parent' => $parent,
            'other' => []
        ];

        $schemaItem = $this->generateEntitySchemaTemplate($moduleCode, $parentModuleConfig);

        $schemaModel = model(SchemaModel::class);
        $moduleRelationModel = model(ModuleRelationModel::class);

        // 先写入schema
        $schemaItem["schema"]['module_id'] = $moduleMap[$schemaItem["schema"]['code']]['id'];
        $schemaAddResData = $schemaModel->addItem($schemaItem["schema"]);

        foreach ($schemaItem["module_relation"] as $moduleRelationItem) {
            $moduleRelationData = $this->generateNodeConfig($schemaAddResData['id'], $moduleRelationItem, $moduleMap);
            $moduleRelationModel->add($moduleRelationData);
        }
    }

    /**
     * 创建新的实体数据结构
     * @param $param
     * @return mixed
     * @throws \Exception
     */
    public function createNewEntitySchema($param)
    {
        // 获取实体表格字段
        $fieldsModel = model(FieldModel::class);
        $moduleModel = model(ModuleModel::class);

        $entityFields = $fieldsModel->getFields('entity');

        $moduleMap = Module::$moduleDictData['module_index_by_code'];


        $moduleModel->startTrans();
        try {
            // 创建模块

            // 模块名称
            if (array_key_exists('name', $param) && !empty($param['name'])) {
                $moduleName = $param['name'];
            } else {
                $moduleName = $this->camelize($param['code']);
            }

            // 图标
            $moduleIcon = '';
            if (array_key_exists('icon', $param) && !empty($param['icon'])) {
                $moduleIcon = $param['icon'];
            }

            // 激活状态
            $moduleActive = 'yes';
            if (array_key_exists('active', $param) && !empty($param['active'])) {
                $moduleActive = $param['active'];
            }

            $moduleAddData = [
                'type' => 'entity',
                'active' => $moduleActive,
                'name' => $moduleName,
                'code' => $param['code'],
                'icon' => $moduleIcon,
                'uuid' => create_uuid()
            ];

            $moduleAddResData = $moduleModel->addItem($moduleAddData);

            if (!$moduleAddResData) {
                // 通用修改失败错误码 001
                throw new \Exception($moduleModel->getError(), ErrorCode::MODULE_ADD_FAILURE);
            }

            $moduleMap[$moduleAddResData['code']] = $moduleAddResData;

            // 创建固定字段配置
            $tempConfig = [];
            foreach ($entityFields as $field => $fieldParam) {
                $tempConfig[] = $this->generateFieldConfig('Entity', $param["code"], $moduleAddResData['id'], $field, $fieldParam);
            }

            $fieldsAddData = [
                'table' => 'entity',
                'module_id' => $moduleAddResData['id'],
                'config' => json_encode($tempConfig),
                'uuid' => create_uuid()
            ];

            $fieldsAddId = $fieldsModel->add($fieldsAddData);

            if ($fieldsAddId > 0) {
                // 创建自定义字段
                $customFieldsAddRows = $this->generateDefaultCustomFields($moduleAddResData['id'], $moduleAddResData['code'], $moduleMap);
                $fieldsModel->addAll($customFieldsAddRows);

                // 创建当前模块 schema
                $this->createSchema($moduleAddResData['code'], $param, $moduleMap);
            }

            $moduleModel->commit();

            return $moduleAddResData;
        } catch (\Exception $e) {
            $moduleModel->rollback();
            throw_strack_exception($e->getMessage(), $e->getCode());
        }
    }

    /**
     * 删除实体模块
     * @param $moduleId
     * @return mixed
     */
    public function deleteEntityModule($moduleId)
    {
        // 首先判断 moduleid 是否存在
        if (!array_key_exists($moduleId, Module::$moduleDictData['module_index_by_id'])) {
            throw_strack_exception('Module does not exist.', ErrorCode::MODULE_NOT_EXIST);
        }

        // 固定模块不允许删除
        if (Module::$moduleDictData['module_index_by_id'][$moduleId]['type'] === 'fixed') {
            throw_strack_exception('Fixed modules are not allowed to be deleted.', ErrorCode::FIXED_MODULE_NOT_ALLOWED_DELETE);
        }

        // 实体模块 asset、level、episode、sequence、shot 不允许删除
        if (Module::$moduleDictData['module_index_by_id'][$moduleId]['type'] === 'entity'
            && in_array(Module::$moduleDictData['module_index_by_id'][$moduleId]['code'], ['asset', 'level', 'episode', 'sequence', 'shot'])
        ) {
            throw_strack_exception('Entity modules asset, level, episode, sequence, shot are not allowed to be deleted.', ErrorCode::ANIMATION_ENTITY_MODULE_NOT_ALLOWED_DELETE);
        }

        // 简单不能删除原则，存在实体，存在任务
        $entityModel = model(EntityModel::class);
        $taskModel = model(TaskModel::class);

        $entityFindFirstId = $entityModel->field('id')->where(['module_id' => $moduleId])->find();
        if ($entityFindFirstId > 0) {
            // 模块存在实体数据不允许删除
            throw_strack_exception('Module has entity data and cannot be deleted.', ErrorCode::MODULE_EXISTING_DATA_NOT_ALLOWED_DELETE);
        }

        $taskFindFirstId = $taskModel->field('id')->where(['entity_module_id' => $moduleId])->find();
        if ($taskFindFirstId > 0) {
            // 模块存在任务数据不允许删除
            throw_strack_exception('Module has task data and cannot be deleted.', ErrorCode::MODULE_EXISTING_DATA_NOT_ALLOWED_DELETE);
        }

        // 删除实体模块数据
        $schemaModel = model(SchemaModel::class);
        $schemaId = $schemaModel->where(['module_id' => $moduleId])->getField('id');

        $moduleRelationModel = model(ModuleRelationModel::class);
        $schemaModel = model(SchemaModel::class);
        $fieldModel = model(FieldModel::class);
        $moduleModel = model(ModuleModel::class);

        $moduleRelationModel->startTrans();
        try {
            // 1、先删除 module_relation 数据
            $moduleRelationModel->where(['schema_id' => $schemaId])->delete();

            // 2、删除 schema
            $schemaModel->where(['id' => $schemaId])->delete();

            // 3、删除 fields
            $fieldModel->where(['module_id' => $moduleId])->delete();

            // 4、删除 module
            $moduleModel->where(['id' => $moduleId])->delete();

            $moduleRelationModel->commit();
        } catch (\Exception $e) {
            $moduleRelationModel->rollback();
            throw_strack_exception($e->getMessage(), $e->getCode());
        }

        return $moduleId;
    }

    /**
     * 获得模块属性字段
     * @param string $moduleCode
     * @return array
     */
    public function getModuleAttributeFieldsWithAttributeIdMap(string $moduleCode)
    {
        $customFields = Module::$moduleDictData['field_index_by_code'][$moduleCode]['custom'];

        $fields = [];
        foreach ($customFields as $item) {
            if (!empty($item['data_source']['attribute_id'])) {
                $fields[$item['field']] = $item['data_source']['attribute_id'];
            }
        }
        return $fields;
    }

    /**
     * 查询属性与字段的map关系
     * @param string $moduleCode
     * @return array
     */
    public function getModuleAttributeCodeFieldsMap(string $moduleCode)
    {
        $fieldsAttributeMap = $this->getModuleAttributeFieldsWithAttributeIdMap($moduleCode);
        $attributeIdList = array_values($fieldsAttributeMap);
        $attributeList = AttributeService::getInstance()->getAttributeList(['id' => ['IN', implode(',', $attributeIdList)]], '*');
        $attributeList = array_column($attributeList, 'code', 'id');
        foreach ($fieldsAttributeMap as &$attributeId) {
            $attributeId = $attributeList[$attributeId] ?? null;
        }
        return $fieldsAttributeMap;
    }

    /**
     * 获得模块默认属性
     * @param $moduleCode
     * @return array
     */
    public function getModuleDefaultAttr($moduleCode)
    {
        $currentModule = module_by_code($moduleCode);
        $currentCustomFields = Module::$moduleDictData['field_index_by_code'][$currentModule['code']]['custom'] ?? [];
        $defaultAttrMap = [];
        $attributeMap = [];
        foreach ($currentCustomFields as $field) {
            if ($field['type'] === 'attribute' && $field['data_source'] && $field['data_source']['type'] === 'api' && isset($field['data_source']['attribute_id']) && $field['data_source']['attribute_id'] > 0) {
                $attributeMap[$field['field']] = $field['data_source']['attribute_id'];
            }
        }
        if (empty($attributeMap)) {
            return $defaultAttrMap;
        }
        $attrList = AttributeValueService::getInstance()->getList(['attribute_id' => ['IN', implode(',', $attributeMap)], 'is_default' => 'yes'], 'value,attribute_id,variable');
        if (empty($attrList)) {
            return $defaultAttrMap;
        }
        $attributeMapFlip = array_flip($attributeMap);
        $attrList = array_group_by($attrList, 'attribute_id');
        foreach ($attrList as $attributeId => $attributeValueList) {
            $field = $attributeMapFlip[$attributeId];
            $attributeValueList = array_sort_by($attributeValueList, 'variable', 'ASC', false);
            $defaultAttrMap[$field] = $attributeValueList[0]['value'];
        }
        return $defaultAttrMap;
    }
}
